using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using Magiq.Behaviors;

namespace Magiq.Update {
    public class UpdateDefinition<T> : BehaviorBasedExecution<IUpdateBehavior<T>>, IUpdateDefinition<T>, IUpdateBehavior<T> where T : class
    {
        public UpdateDefinition(IEnumerable<T> queryable,IMagiqBehavior behavior ) : base(behavior)
        {
            Source = queryable;
            propertySets = new List<IPropertySet>();
        }

        #region IUpdateDefinition<T> Members

        int IUpdateDefinition<T>.Update()
        {
            if (propertySets.Count == 0)
            {
                var exception = new NoPropertiesToUpdateException();
                Call<IErrorBehavior>(x => x.OnException(exception));
                throw exception;
            }

            var strategy = Call(x => x.GetUpdateStrategy(this));
            var result = Call(x => x.Execute(strategy));
            return result;
        }

        public IEnumerable<T> Source { get; private set; }

        #endregion

        private readonly List<IPropertySet> propertySets;
        public IEnumerable<IPropertySet> PropertySets {
            get { return propertySets; }
            
        }

        #region IUpdateDefinition<T> Members

        public IUpdateDefinition<T> Set<TReturn>(Expression<Func<T, TReturn>> property, TReturn value) {
            Set(property, x => value);
            return this;
        }

        public virtual IUpdateDefinition<T> Set<TReturn>(Expression<Func<T, TReturn>> property, Expression<Func<T, TReturn>> value) {
            propertySets.Add(new PropertySet(property, value));
            return this;
        }

        #endregion

        protected void AddPorpertySets(IEnumerable<IPropertySet> sets) {
            propertySets.AddRange(sets);
        }

        IUpdateStrategy IUpdateBehavior<T>.GetUpdateStrategy(IUpdateDefinition<T> updateDefinition)
        {
            return Magiq.ForUpdate(updateDefinition);
        }

        int IUpdateBehavior<T>.Execute(IUpdateStrategy strategy)
        {
            return strategy.Execute();
        }
    }
}